﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.ComponentModel;
using System.Text;
using System.Threading;
using BrainTechLLC.ThreadSafeObjects;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.Logging
{
    /// <summary>
    /// Abstract base class used to easily create other ILogger implementations
    /// </summary>
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
    [Serializable]
    [DataContract]
    public abstract class LoggerBase : ILogger
    {
        public event EventHandler<ObjectEventArgs<LogMessage>> LoggedMessage;

        /// <summary>
        /// Stores log lines waiting to be output
        /// </summary>
        protected ThreadSafeQueue<LogMessage> _logOutput;

        protected LogSeverity _logFilter = LogSeverity.All;
        protected ThreadSafeList<ILogListener> _logListeners;
        public LogSeverity LogFilter { get { return _logFilter; } }
        public bool WantExit { get; set; }

        public LoggerBase()
        {
            _logOutput = new ThreadSafeQueue<LogMessage>();
            _logListeners = new ThreadSafeList<ILogListener>();
            _logFilter = LogSeverity.All;
        }

        protected virtual void EnqueueNewLogLine(LogMessage logLine)
        {
            _logOutput.Enqueue(logLine);

            OnLoggedMessage(logLine);
        }

        public void OnLoggedMessage(LogMessage message)
        {
            if (LoggedMessage != null)
                LoggedMessage(this, new ObjectEventArgs<LogMessage>(message));
        }

        public virtual void BeginLogging()
        {
            ThreadStart ts = new ThreadStart(LogListener);
            Thread logThread = ts.CreateAndRunThread();
        }

        protected abstract void LogListener();

        /// <summary>
        /// Lets the programmer turn on or off logging of specific severity levels
        /// </summary>
        /// <param name="severity"></param>
        /// <param name="logThisSeverityLevel"></param>
        public void SetFilter(LogSeverity severity, bool logThisSeverityLevel)
        {
            if (logThisSeverityLevel)
                _logFilter = _logFilter ^= ~severity;
            else
                _logFilter |= severity;
        }

        public bool AddLogListener(ILogListener listener) { if (_logListeners.Find(listener) != null) return false; _logListeners.Add(listener); return true; }
        public bool RemoveLogListener(ILogListener listener) { return _logListeners.Remove(listener); }

        public void Log(LogMessage message)
        {
            DateTime now = DateTime.Now;
            string nowString = now.ToString();
            
            EnqueueNewLogLine(message);

            SimpleEventArgs<LogMessage> eventArgs = null;

            for (int n = 0; n < _logListeners.Count; n++)
            {
                ILogListener listener = _logListeners.GetItem(n);
                if (listener == null) break;
                if (listener.IsListening)
                {
                    if (eventArgs == null) eventArgs = new SimpleEventArgs<LogMessage>(message);
                    listener.OnNewLogEntry(eventArgs);
                }
            }
        }

        public void Log(string messageType, string message, LogSeverity severity)
        {
            if ((_logFilter & severity) == severity)
            {
                LogMessage msg = new LogMessage()
                {
                    MessageType = messageType,
                    Message = message,
                    Severity = severity,
                    LogTime = DateTime.Now
                };

                Log(msg);
            }
        }

        public void Log(Exception ex, LogSeverity severity, bool verbose)
        {
            Log("Exception", (verbose) ?
                ((ex.StackTrace == null ? "" : ex.StackTrace) + Environment.NewLine + ex.ToString()) :
                ex.Message, severity);
        }

        public void Log(Exception ex, string message, LogSeverity severity, bool verbose)
        {
            Log("Exception: " + message, (verbose) ?
                ((ex.StackTrace == null ? "" : ex.StackTrace) + Environment.NewLine + ex.ToString()) :
                ex.Message, severity);
        }

        private string FormatLogEntry(LogMessage message)
        {
            return message.ToString();
        }        
    }    
}
